package clustering;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.internal.core.util.InnerClassesAttribute;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;

public class MethodRelationshipDeterminer {
	
	class FieldsVisitor extends ASTVisitor {
		
		private List<IVariableBinding> _fields = new LinkedList<IVariableBinding>();
		
		@Override
		public boolean visit(SimpleName node) {
			IBinding parentMethodOrTypeBinding = ASTResolving.getParentMethodOrTypeBinding(node);
			ITypeBinding type;
			if (parentMethodOrTypeBinding instanceof IMethodBinding)
				type = ((IMethodBinding)parentMethodOrTypeBinding).getDeclaringClass();
			else
				type = (ITypeBinding)parentMethodOrTypeBinding;
			IVariableBinding field = Bindings.findFieldInType(type, node.getFullyQualifiedName());
			if (field !=null)
				_fields.add(field);
			return super.visit(node);
		}
		
		public List<IVariableBinding> getFields() {
			return _fields;
		}
	}

	public boolean areRelated(MethodDeclaration method1, MethodDeclaration method2) {
		if (!method1.resolveBinding().getDeclaringClass().isEqualTo(method2.resolveBinding().getDeclaringClass())) {
			System.out.println("Error:classification of methods of different classes");
			return false;
		}
		if (method1.resolveBinding().isEqualTo(method2.resolveBinding())) {
			System.out.println("Error:classification of 2copies of 1 method");
			return true;
		}
		/*First check if methods share fields*/
		FieldsVisitor fieldsVisitor1 = new FieldsVisitor();
		method1.accept(fieldsVisitor1);
		FieldsVisitor fieldsVisitor2 = new FieldsVisitor();
		method2.accept(fieldsVisitor2);
		List<IVariableBinding> fields1 = fieldsVisitor1.getFields();
		List<IVariableBinding> fields2 = fieldsVisitor2.getFields();
		for (IVariableBinding fieldUsedInMethod1 : fields1)
			for (IVariableBinding fieldUsedInMethod2 : fields2)
				if (fieldUsedInMethod1.isEqualTo(fieldUsedInMethod2))
					return true;
		/*Check whether methods are part of one interface*/
		ITypeBinding declaringClass = method1.resolveBinding().getDeclaringClass();
		for (ITypeBinding implementedInterface: declaringClass.getInterfaces()) {
			boolean firstIsSubsignature = false;
			boolean secondIsSubsignature = false;
			for (IMethodBinding implementedInterfaceMethod : implementedInterface.getDeclaredMethods()) {
				if (method1.resolveBinding().isSubsignature(implementedInterfaceMethod))
					firstIsSubsignature = true;
				if (method2.resolveBinding().isSubsignature(implementedInterfaceMethod))
					secondIsSubsignature = true;
			}
			if (firstIsSubsignature && secondIsSubsignature)
				return true;
		}
		/*Check whether methods are part of one superclass*/
		for (IMethodBinding implementedInterfaceMethod : declaringClass.getSuperclass().getDeclaredMethods()) {
			if (method1.resolveBinding().isSubsignature(implementedInterfaceMethod)
				&&method2.resolveBinding().isSubsignature(implementedInterfaceMethod))
				return true;
		}
		return false;
	}
}
